﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using FarseerPhysics.DebugViews;

using ZoeEngine.Components.Physics;
using ZoeEngine.Services.Scene;
using ZoeEngine.Services.Content;
using ZoeEngine.Framework.Rendering;

namespace ZoeEngine.Services.Physics
{
    public class PhysicsProvider : DrawableGameComponent, IPhysicsService
    {
        private World _world = null;
        private Vector2 _gravity = new Vector2(0.0f, 9.0f);
        
        private DebugViewXNA _debugView = null;
        private ICamera _sceneCamera = null;

        public PhysicsProvider(Game game)
            : base(game)
        {
            if (game.Services.GetService(typeof(IPhysicsService)) != null)
            {
                throw new ArgumentException("Physics provider already exists.");
            }
            game.Services.AddService(typeof(IPhysicsService), this);

            _world = new World(_gravity);
        }

        public List<BaseSpatialComponent> GetObjectsUnderPoint(Point point)
        {
            List<BaseSpatialComponent> results = new List<BaseSpatialComponent>();
            
            List<Fixture> hits = _world.TestPointAll(Utility.ToMeters(new Vector2(point.X, point.Y)));
            foreach (Fixture hitFix in hits)
            {
                BaseSpatialComponent hitSpatialComp = hitFix.Body.UserData as BaseSpatialComponent;
                if(!results.Contains(hitSpatialComp))
                {
                    results.Add(hitSpatialComp);
                }
            }
            return results;
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            // Set up DebugView object.
            _debugView = new DebugViewXNA(_world);
            _debugView.AppendFlags(DebugViewFlags.Shape | DebugViewFlags.PolygonPoints | DebugViewFlags.Joint | DebugViewFlags.PerformanceGraph);
            _debugView.DebugPanelPosition = new Vector2(5, 5);
            _debugView.DefaultShapeColor = Color.White;
            _debugView.SleepingShapeColor = Color.LightGray;
            _debugView.LoadContent(Game.GraphicsDevice, Game.Content);

            // Retrieve reference to the active scene camera for view matrix.
            _sceneCamera = (Game.Services.GetService(typeof(ISceneService)) as ISceneService).SceneCamera;
        }
        public override void Initialize()
        {
            base.Initialize();
        }
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            _world.Step((float)(gameTime.ElapsedGameTime.TotalMilliseconds * 0.001));
        }
        public override void Draw(GameTime gameTime)
        {
            if (Zoe.DEBUG_MODE.HasFlag(DebugMode.PHYSICS))
            {
                Matrix orthoProjection = Matrix.CreateOrthographicOffCenter(0.0f, Utility.ToMeters(GraphicsDevice.Viewport.Width), 0.0f, Utility.ToMeters(GraphicsDevice.Viewport.Height), 0.0f, 1000000.0f) * Matrix.CreateScale(1, -1, 1);
               _debugView.RenderDebugData(ref orthoProjection);
            }
            base.Draw(gameTime);
        }

        public Vector2 Gravity
        {
            get
            {
                return Utility.ToPixels(_gravity);
            }
            set
            {
                _gravity = Utility.ToMeters(value);
                if (_world != null)
                {
                    _world.Gravity = Utility.ToMeters(_gravity);
                }
            }
        }
        internal World World
        {
            get
            {
                return _world;
            }
        }
    }
}
